package org.moonlight.jvm.instructions.references;

import org.moonlight.jvm.instructions.base.Index16Instruction;
import org.moonlight.jvm.rtda.exclusive.Frame;
import org.moonlight.jvm.rtda.exclusive.OperandStack;
import org.moonlight.jvm.rtda.share.heap.Class;
import org.moonlight.jvm.rtda.share.heap.classmember.Field;
import org.moonlight.jvm.rtda.share.heap.classmember.Method;
import org.moonlight.jvm.rtda.share.heap.symref.FieldRef;
import org.moonlight.jvm.rtda.share.methodarea.RtConstantPool;
import org.moonlight.jvm.rtda.share.Object;

/**
 * PutField: 给实例变量赋值。它需要三个操作数:
 *           1. 常量池索引 - 来自操作数，一个 uint16 索引
 *           2. 变量值 - 从操作数栈弹出
 *           3. 对象引用 - 从操作数栈弹出
 *
 * @author Moonlight
 * @createTime 2023/8/30 15:02
 **/
public class PutField extends Index16Instruction {

    @Override
    public void execute(Frame frame) {
        // 当前方法
        Method curMethod = frame.getMethod();
        // 当前类
        Class curClazz = curMethod.getClazz();
        // 当前运行时常量池
        RtConstantPool rtCp = curClazz.getRtCp();
        // 通过 uint16索引 获取实例字段符号引用
        FieldRef fieldRef = (FieldRef) rtCp.getConstant(this.idx);
        // 解析出字段
        Field field = fieldRef.resolvedField();
        // 解析后的字段必须是实例字段，否则抛出IncompatibleClassChangeError
        if (field.isStatic()) {
            throw new IncompatibleClassChangeError();
        }
        // 如果是final字段，则只能在构造函数中初始化，否则抛出IllegalAccessError
        if (field.isFinal()) {
            if (!curClazz.equals(field.getClazz()) || !"<init>".equals(curMethod.getName())) {
                throw new IllegalAccessError();
            }
        }

        String desc = field.getDescriptor();
        int slotId = field.getSlotId();
        OperandStack operandStack = frame.getOperandStack();

        switch (desc.substring(0, 1)) {
            case "Z":
            case "B":
            case "C":
            case "S":
            case "I":
                int valInt = operandStack.popInt();
                Object refInt = operandStack.popRef();
                if (refInt == null) {
                    throw new NullPointerException();
                }
                refInt.getFields().setInt(slotId, valInt);
                break;
            case "F":
                float valFloat = operandStack.popFloat();
                Object refFloat = operandStack.popRef();
                if (refFloat == null) {
                    throw new NullPointerException();
                }
                refFloat.getFields().setFloat(slotId, valFloat);
                break;
            case "J":
                long valLong = operandStack.popLong();
                Object refLong = operandStack.popRef();
                if (refLong == null) {
                    throw new NullPointerException();
                }
                refLong.getFields().setLong(slotId, valLong);
                break;
            case "D":
                double valDouble = operandStack.popDouble();
                Object refDouble = operandStack.popRef();
                if (refDouble == null) {
                    throw new NullPointerException();
                }
                refDouble.getFields().setDouble(slotId, valDouble);
                break;
            case "L":
            case "[":
                Object valRef = operandStack.popRef();
                Object refRef = operandStack.popRef();
                if (refRef == null) {
                    throw new NullPointerException();
                }
                refRef.getFields().setRef(slotId, valRef);
                break;
            default:
                throw new RuntimeException("不支持的描述符: " + desc);
        }
    }

}
